package com.example.docmanagement.service;

import com.example.docmanagement.dto.request.RoleCreateRequest;
import com.example.docmanagement.dto.request.RoleQueryRequest;
import com.example.docmanagement.dto.request.RoleUpdateRequest;
import com.example.docmanagement.dto.request.UpdateRolePermissionsRequest;
import com.example.docmanagement.dto.response.PageResponse;
import com.example.docmanagement.dto.response.RoleResponse;
import com.example.docmanagement.dto.response.UserResponse;
import com.example.docmanagement.entity.Permission;
import com.example.docmanagement.entity.Role;
import com.example.docmanagement.entity.User;
import com.example.docmanagement.exception.DuplicateResourceException;
import com.example.docmanagement.exception.ResourceNotFoundException;
import com.example.docmanagement.repository.PermissionRepository;
import com.example.docmanagement.repository.RoleRepository;
import com.example.docmanagement.repository.UserRepository;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 管理后台 - 角色管理服务
 */
@Service
@RequiredArgsConstructor
public class RoleAdminService {

    private final RoleRepository roleRepository;
    private final PermissionRepository permissionRepository;
    private final UserRepository userRepository;
    private final UserAdminService userAdminService;

    /**
     * 分页查询角色
     */
    @Transactional(readOnly = true)
    public PageResponse<RoleResponse> searchRoles(RoleQueryRequest request) {
        Pageable pageable = buildPageable(request);
        Specification<Role> specification = buildSpecification(request);
        Page<Role> page = roleRepository.findAll(specification, pageable);

        List<RoleResponse> data = page.getContent().stream()
                .map(this::toResponse)
                .collect(Collectors.toList());

        return PageResponse.<RoleResponse>builder()
                .data(data)
                .page(request.getPage())
                .pageSize(request.getPageSize())
                .total(page.getTotalElements())
                .totalPages(page.getTotalPages())
                .hasNext(page.hasNext())
                .hasPrevious(page.hasPrevious())
                .build();
    }

    /**
     * 获取角色详情
     */
    @Transactional(readOnly = true)
    public RoleResponse getRole(Long id) {
        Role role = roleRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        return toResponse(role);
    }

    /**
     * 创建角色
     */
    @Transactional
    public RoleResponse createRole(RoleCreateRequest request) {
        if (roleRepository.existsByName(request.getName())) {
            throw new DuplicateResourceException("角色名称已存在", request.getName());
        }

        Role role = new Role();
        role.setName(request.getName());
        role.setDisplayName(request.getDisplayName());
        role.setDescription(request.getDescription());
        role.setSortOrder(Optional.ofNullable(request.getSortOrder()).orElse(0));
        role.setIsSystem(Boolean.TRUE.equals(request.getSystemRole()));

        if (request.getPermissionIds() != null && !request.getPermissionIds().isEmpty()) {
            List<Permission> permissions = permissionRepository.findAllById(request.getPermissionIds());
            role.setPermissions(new HashSet<>(permissions));
        } else {
            role.setPermissions(new HashSet<>());
        }

        role = roleRepository.save(role);
        return toResponse(role);
    }

    /**
     * 更新角色
     */
    @Transactional
    public RoleResponse updateRole(Long id, RoleUpdateRequest request) {
        Role role = roleRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));

        if (request.getDisplayName() != null) {
            role.setDisplayName(request.getDisplayName());
        }
        if (request.getDescription() != null) {
            role.setDescription(request.getDescription());
        }
        if (request.getSortOrder() != null) {
            role.setSortOrder(request.getSortOrder());
        }
        if (request.getSystemRole() != null) {
            role.setIsSystem(request.getSystemRole());
        }
        if (request.getPermissionIds() != null) {
            List<Permission> permissions = permissionRepository.findAllById(request.getPermissionIds());
            role.setPermissions(new HashSet<>(permissions));
        }

        role = roleRepository.save(role);
        return toResponse(role);
    }

    /**
     * 更新角色权限
     */
    @Transactional
    public RoleResponse updateRolePermissions(Long id, UpdateRolePermissionsRequest request) {
        Role role = roleRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        List<Permission> permissions = permissionRepository.findAllById(request.getPermissionIds());
        role.setPermissions(new HashSet<>(permissions));
        role = roleRepository.save(role);
        return toResponse(role);
    }

    /**
     * 删除角色
     */
    @Transactional
    public void deleteRole(Long id) {
        Role role = roleRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        if (Boolean.TRUE.equals(role.getIsSystem())) {
            throw new IllegalStateException("系统角色不可删除");
        }

        long count = userRepository.count((Specification<User>) (root, query, cb) -> cb.equal(root.get("roleId"), id));
        if (count > 0) {
            throw new IllegalStateException("该角色仍有关联用户，无法删除");
        }

        roleRepository.delete(role);
    }

    /**
     * 查询角色下的用户
     */
    @Transactional(readOnly = true)
    public PageResponse<UserResponse> listRoleUsers(Long roleId, Integer page, Integer pageSize) {
        roleRepository.findById(roleId)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        Pageable pageable = PageRequest.of(Math.max(page - 1, 0), Math.min(Math.max(pageSize, 1), 200), Sort.by(Sort.Direction.DESC, "createdAt"));
        Specification<User> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("roleId"), roleId));
            predicates.add(cb.isNull(root.get("deletedAt")));
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Page<User> userPage = userRepository.findAll(spec, pageable);
        List<UserResponse> data = userPage.getContent().stream()
                .map(user -> userAdminService.getUser(user.getId()))
                .collect(Collectors.toList());

        return PageResponse.<UserResponse>builder()
                .data(data)
                .page(page)
                .pageSize(pageSize)
                .total(userPage.getTotalElements())
                .totalPages(userPage.getTotalPages())
                .hasNext(userPage.hasNext())
                .hasPrevious(userPage.hasPrevious())
                .build();
    }

    /**
     * 批量为角色添加用户
     */
    @Transactional
    public void addUsersToRole(Long roleId, List<UUID> userIds) {
        if (userIds == null || userIds.isEmpty()) {
            return;
        }
        Role role = roleRepository.findById(roleId)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        List<User> users = userRepository.findAllById(userIds);
        if (users.isEmpty()) {
            throw new ResourceNotFoundException("未找到可分配的用户");
        }
        Set<UUID> uniqueIds = new HashSet<>(userIds);
        if (uniqueIds.size() != users.size()) {
            throw new ResourceNotFoundException("部分用户不存在或已被删除");
        }
        for (User user : users) {
            if (user.getDeletedAt() != null) {
                throw new IllegalStateException("用户 " + user.getEmail() + " 已被删除，无法分配角色");
            }
            user.setRoleId(role.getId());
            user.setRole(null);
        }
        userRepository.saveAll(users);
    }

    /**
     * 批量移除角色下的用户
     */
    @Transactional
    public void removeUsersFromRole(Long roleId, List<UUID> userIds) {
        if (userIds == null || userIds.isEmpty()) {
            return;
        }
        roleRepository.findById(roleId)
                .orElseThrow(() -> new ResourceNotFoundException("角色不存在"));
        List<User> users = userRepository.findAllById(userIds);
        if (users.isEmpty()) {
            return;
        }
        for (User user : users) {
            if (!roleId.equals(user.getRoleId())) {
                continue;
            }
            user.setRoleId(null);
            user.setRole(null);
        }
        userRepository.saveAll(users);
    }

    // ==================== 私有工具 ====================

    private Specification<Role> buildSpecification(RoleQueryRequest request) {
        return (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (request.getKeyword() != null && !request.getKeyword().isEmpty()) {
                String keyword = "%" + request.getKeyword().toLowerCase(Locale.ROOT) + "%";
                Predicate namePredicate = cb.like(cb.lower(root.get("name")), keyword);
                Predicate displayPredicate = cb.like(cb.lower(root.get("displayName")), keyword);
                predicates.add(cb.or(namePredicate, displayPredicate));
            }

            if (request.getSystemRole() != null) {
                predicates.add(cb.equal(root.get("isSystem"), request.getSystemRole()));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        };
    }

    private Pageable buildPageable(RoleQueryRequest request) {
        int page = Math.max(0, request.getPage() - 1);
        int size = Math.min(Math.max(request.getPageSize(), 1), 200);
        Sort.Direction direction = "desc".equalsIgnoreCase(request.getSortOrder()) ? Sort.Direction.DESC : Sort.Direction.ASC;
        String sortProperty = "sortOrder";
        if ("createdAt".equalsIgnoreCase(request.getSortBy())) {
            sortProperty = "createdAt";
        }
        Sort sort = Sort.by(direction, sortProperty);
        if (!"createdAt".equals(sortProperty)) {
            sort = sort.and(Sort.by(Sort.Direction.DESC, "createdAt"));
        }
        return PageRequest.of(page, size, sort);
    }

    private RoleResponse toResponse(Role role) {
        List<Permission> permissions = new ArrayList<>(Optional.ofNullable(role.getPermissions())
                .orElseGet(HashSet::new));
        permissions.sort(Comparator.comparing(Permission::getSortOrder, Comparator.nullsLast(Integer::compareTo))
                .thenComparing(Permission::getName));

        List<Long> permissionIds = permissions.stream()
                .map(Permission::getId)
                .collect(Collectors.toList());

        List<com.example.docmanagement.dto.response.PermissionResponse> permissionResponses = permissions.stream()
                .map(permission -> com.example.docmanagement.dto.response.PermissionResponse.builder()
                        .id(permission.getId())
                        .name(permission.getName())
                        .displayName(permission.getDisplayName())
                        .description(permission.getDescription())
                        .resource(permission.getResource())
                        .action(permission.getAction())
                        .category(permission.getCategory())
                        .sortOrder(permission.getSortOrder())
                        .createdAt(permission.getCreatedAt())
                        .build())
                .collect(Collectors.toList());

        return RoleResponse.builder()
                .id(role.getId())
                .name(role.getName())
                .displayName(role.getDisplayName())
                .description(role.getDescription())
                .systemRole(role.getIsSystem())
                .sortOrder(role.getSortOrder())
                .createdAt(role.getCreatedAt())
                .updatedAt(role.getUpdatedAt())
                .permissionIds(permissionIds)
                .permissions(permissionResponses)
                .build();
    }
}


